home *** CD-ROM | disk | FTP | other *** search
- <?php
- /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
-
- /**
- * HTTP::Download
- *
- * PHP versions 4 and 5
- *
- * @category HTTP
- * @package HTTP_Download
- * @author Michael Wallner <mike@php.net>
- * @copyright 2003-2005 Michael Wallner
- * @license BSD, revised
- * @version CVS: $Id: Download.php,v 1.76 2005/11/28 15:28:00 mike Exp $
- * @link http://pear.php.net/package/HTTP_Download
- */
-
- // {{{ includes
- /**
- * Requires PEAR
- */
- require_once 'PEAR.php';
-
- /**
- * Requires HTTP_Header
- */
- require_once 'HTTP/Header.php';
- // }}}
-
- // {{{ constants
- /**#@+ Use with HTTP_Download::setContentDisposition() **/
- /**
- * Send data as attachment
- */
- define('HTTP_DOWNLOAD_ATTACHMENT', 'attachment');
- /**
- * Send data inline
- */
- define('HTTP_DOWNLOAD_INLINE', 'inline');
- /**#@-**/
-
- /**#@+ Use with HTTP_Download::sendArchive() **/
- /**
- * Send as uncompressed tar archive
- */
- define('HTTP_DOWNLOAD_TAR', 'TAR');
- /**
- * Send as gzipped tar archive
- */
- define('HTTP_DOWNLOAD_TGZ', 'TGZ');
- /**
- * Send as bzip2 compressed tar archive
- */
- define('HTTP_DOWNLOAD_BZ2', 'BZ2');
- /**
- * Send as zip archive
- */
- define('HTTP_DOWNLOAD_ZIP', 'ZIP');
- /**#@-**/
-
- /**#@+
- * Error constants
- */
- define('HTTP_DOWNLOAD_E_HEADERS_SENT', -1);
- define('HTTP_DOWNLOAD_E_NO_EXT_ZLIB', -2);
- define('HTTP_DOWNLOAD_E_NO_EXT_MMAGIC', -3);
- define('HTTP_DOWNLOAD_E_INVALID_FILE', -4);
- define('HTTP_DOWNLOAD_E_INVALID_PARAM', -5);
- define('HTTP_DOWNLOAD_E_INVALID_RESOURCE', -6);
- define('HTTP_DOWNLOAD_E_INVALID_REQUEST', -7);
- define('HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE', -8);
- define('HTTP_DOWNLOAD_E_INVALID_ARCHIVE_TYPE', -9);
- /**#@-**/
- // }}}
-
- /**
- * Send HTTP Downloads/Responses.
- *
- * With this package you can handle (hidden) downloads.
- * It supports partial downloads, resuming and sending
- * raw data ie. from database BLOBs.
- *
- * <i>ATTENTION:</i>
- * You shouldn't use this package together with ob_gzhandler or
- * zlib.output_compression enabled in your php.ini, especially
- * if you want to send already gzipped data!
- *
- * @access public
- * @version $Revision: 1.76 $
- */
- class HTTP_Download
- {
- // {{{ protected member variables
- /**
- * Path to file for download
- *
- * @see HTTP_Download::setFile()
- * @access protected
- * @var string
- */
- var $file = '';
-
- /**
- * Data for download
- *
- * @see HTTP_Download::setData()
- * @access protected
- * @var string
- */
- var $data = null;
-
- /**
- * Resource handle for download
- *
- * @see HTTP_Download::setResource()
- * @access protected
- * @var int
- */
- var $handle = null;
-
- /**
- * Whether to gzip the download
- *
- * @access protected
- * @var bool
- */
- var $gzip = false;
-
- /**
- * Whether to allow caching of the download on the clients side
- *
- * @access protected
- * @var bool
- */
- var $cache = true;
-
- /**
- * Size of download
- *
- * @access protected
- * @var int
- */
- var $size = 0;
-
- /**
- * Last modified
- *
- * @access protected
- * @var int
- */
- var $lastModified = 0;
-
- /**
- * HTTP headers
- *
- * @access protected
- * @var array
- */
- var $headers = array(
- 'Content-Type' => 'application/x-octetstream',
- 'Pragma' => 'cache',
- 'Cache-Control' => 'public, must-revalidate, max-age=0',
- 'Accept-Ranges' => 'bytes',
- 'X-Sent-By' => 'PEAR::HTTP::Download'
- );
-
- /**
- * HTTP_Header
- *
- * @access protected
- * @var object
- */
- var $HTTP = null;
-
- /**
- * ETag
- *
- * @access protected
- * @var string
- */
- var $etag = '';
-
- /**
- * Buffer Size
- *
- * @access protected
- * @var int
- */
- var $bufferSize = 2097152;
-
- /**
- * Throttle Delay
- *
- * @access protected
- * @var float
- */
- var $throttleDelay = 0;
-
- /**
- * Sent Bytes
- *
- * @access public
- * @var int
- */
- var $sentBytes = 0;
- // }}}
-
- // {{{ constructor
- /**
- * Constructor
- *
- * Set supplied parameters.
- *
- * @access public
- * @param array $params associative array of parameters
- *
- * <b>one of:</b>
- * o 'file' => path to file for download
- * o 'data' => raw data for download
- * o 'resource' => resource handle for download
- * <br/>
- * <b>and any of:</b>
- * o 'cache' => whether to allow cs caching
- * o 'gzip' => whether to gzip the download
- * o 'lastmodified' => unix timestamp
- * o 'contenttype' => content type of download
- * o 'contentdisposition' => content disposition
- * o 'buffersize' => amount of bytes to buffer
- * o 'throttledelay' => amount of secs to sleep
- * o 'cachecontrol' => cache privacy and validity
- *
- * <br />
- * 'Content-Disposition' is not HTTP compliant, but most browsers
- * follow this header, so it was borrowed from MIME standard.
- *
- * It looks like this: <br />
- * "Content-Disposition: attachment; filename=example.tgz".
- *
- * @see HTTP_Download::setContentDisposition()
- */
- function HTTP_Download($params = array())
- {
- $this->HTTP = &new HTTP_Header;
- $this->setParams($params);
- }
- // }}}
-
- // {{{ public methods
- /**
- * Set parameters
- *
- * Set supplied parameters through its accessor methods.
- *
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param array $params associative array of parameters
- *
- * @see HTTP_Download::HTTP_Download()
- */
- function setParams($params)
- {
- foreach((array) $params as $param => $value){
- $method = 'set'. $param;
-
- if (!method_exists($this, $method)) {
- return PEAR::raiseError(
- "Method '$method' doesn't exist.",
- HTTP_DOWNLOAD_E_INVALID_PARAM
- );
- }
-
- $e = call_user_func_array(array(&$this, $method), (array) $value);
-
- if (PEAR::isError($e)) {
- return $e;
- }
- }
- return true;
- }
-
- /**
- * Set path to file for download
- *
- * The Last-Modified header will be set to files filemtime(), actually.
- * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_FILE) if file doesn't exist.
- * Sends HTTP 404 status if $send_404 is set to true.
- *
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param string $file path to file for download
- * @param bool $send_404 whether to send HTTP/404 if
- * the file wasn't found
- */
- function setFile($file, $send_404 = true)
- {
- $file = realpath($file);
- if (!is_file($file)) {
- if ($send_404) {
- $this->HTTP->sendStatusCode(404);
- }
- return PEAR::raiseError(
- "File '$file' not found.",
- HTTP_DOWNLOAD_E_INVALID_FILE
- );
- }
- $this->setLastModified(filemtime($file));
- $this->file = $file;
- $this->size = filesize($file);
- return true;
- }
-
- /**
- * Set data for download
- *
- * Set $data to null if you want to unset this.
- *
- * @access public
- * @return void
- * @param $data raw data to send
- */
- function setData($data = null)
- {
- $this->data = $data;
- $this->size = strlen($data);
- }
-
- /**
- * Set resource for download
- *
- * The resource handle supplied will be closed after sending the download.
- * Returns a PEAR_Error (HTTP_DOWNLOAD_E_INVALID_RESOURCE) if $handle
- * is no valid resource. Set $handle to null if you want to unset this.
- *
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param int $handle resource handle
- */
- function setResource($handle = null)
- {
- if (!isset($handle)) {
- $this->handle = null;
- $this->size = 0;
- return true;
- }
-
- if (is_resource($handle)) {
- $this->handle = $handle;
- $filestats = fstat($handle);
- $this->size = $filestats['size'];
- return true;
- }
-
- return PEAR::raiseError(
- "Handle '$handle' is no valid resource.",
- HTTP_DOWNLOAD_E_INVALID_RESOURCE
- );
- }
-
- /**
- * Whether to gzip the download
- *
- * Returns a PEAR_Error (HTTP_DOWNLOAD_E_NO_EXT_ZLIB)
- * if ext/zlib is not available/loadable.
- *
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param bool $gzip whether to gzip the download
- */
- function setGzip($gzip = false)
- {
- if ($gzip && !PEAR::loadExtension('zlib')){
- return PEAR::raiseError(
- 'GZIP compression (ext/zlib) not available.',
- HTTP_DOWNLOAD_E_NO_EXT_ZLIB
- );
- }
- $this->gzip = (bool) $gzip;
- return true;
- }
-
- /**
- * Whether to allow caching
- *
- * If set to true (default) we'll send some headers that are commonly
- * used for caching purposes like ETag, Cache-Control and Last-Modified.
- *
- * If caching is disabled, we'll send the download no matter if it
- * would actually be cached at the client side.
- *
- * @access public
- * @return void
- * @param bool $cache whether to allow caching
- */
- function setCache($cache = true)
- {
- $this->cache = (bool) $cache;
- }
-
- /**
- * Whether to allow proxies to cache
- *
- * If set to 'private' proxies shouldn't cache the response.
- * This setting defaults to 'public' and affects only cached responses.
- *
- * @access public
- * @return bool
- * @param string $cache private or public
- * @param int $maxage maximum age of the client cache entry
- */
- function setCacheControl($cache = 'public', $maxage = 0)
- {
- switch ($cache = strToLower($cache))
- {
- case 'private':
- case 'public':
- $this->headers['Cache-Control'] =
- $cache .', must-revalidate, max-age='. abs($maxage);
- return true;
- break;
- }
- return false;
- }
-
- /**
- * Set ETag
- *
- * Sets a user-defined ETag for cache-validation. The ETag is usually
- * generated by HTTP_Download through its payload information.
- *
- * @access public
- * @return void
- * @param string $etag Entity tag used for strong cache validation.
- */
- function setETag($etag = null)
- {
- $this->etag = (string) $etag;
- }
-
- /**
- * Set Size of Buffer
- *
- * The amount of bytes specified as buffer size is the maximum amount
- * of data read at once from resources or files. The default size is 2M
- * (2097152 bytes). Be aware that if you enable gzip compression and
- * you set a very low buffer size that the actual file size may grow
- * due to added gzip headers for each sent chunk of the specified size.
- *
- * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_PARAM) if $size is not
- * greater than 0 bytes.
- *
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param int $bytes Amount of bytes to use as buffer.
- */
- function setBufferSize($bytes = 2097152)
- {
- if (0 >= $bytes) {
- return PEAR::raiseError(
- 'Buffer size must be greater than 0 bytes ('. $bytes .' given)',
- HTTP_DOWNLOAD_E_INVALID_PARAM);
- }
- $this->bufferSize = abs($bytes);
- return true;
- }
-
- /**
- * Set Throttle Delay
- *
- * Set the amount of seconds to sleep after each chunck that has been
- * sent. One can implement some sort of throttle through adjusting the
- * buffer size and the throttle delay. With the following settings
- * HTTP_Download will sleep a second after each 25 K of data sent.
- *
- * <code>
- * Array(
- * 'throttledelay' => 1,
- * 'buffersize' => 1024 * 25,
- * )
- * </code>
- *
- * Just be aware that if gzipp'ing is enabled, decreasing the chunk size
- * too much leads to proportionally increased network traffic due to added
- * gzip header and bottom bytes around each chunk.
- *
- * @access public
- * @return void
- * @param float $seconds Amount of seconds to sleep after each
- * chunk that has been sent.
- */
- function setThrottleDelay($seconds = 0)
- {
- $this->throttleDelay = abs($seconds) * 1000;
- }
-
- /**
- * Set "Last-Modified"
- *
- * This is usually determined by filemtime() in HTTP_Download::setFile()
- * If you set raw data for download with HTTP_Download::setData() and you
- * want do send an appropiate "Last-Modified" header, you should call this
- * method.
- *
- * @access public
- * @return void
- * @param int unix timestamp
- */
- function setLastModified($last_modified)
- {
- $this->lastModified = $this->headers['Last-Modified'] = (int) $last_modified;
- }
-
- /**
- * Set Content-Disposition header
- *
- * @see HTTP_Download::HTTP_Download
- *
- * @access public
- * @return void
- * @param string $disposition whether to send the download
- * inline or as attachment
- * @param string $file_name the filename to display in
- * the browser's download window
- *
- * <b>Example:</b>
- * <code>
- * $HTTP_Download->setContentDisposition(
- * HTTP_DOWNLOAD_ATTACHMENT,
- * 'download.tgz'
- * );
- * </code>
- */
- function setContentDisposition( $disposition = HTTP_DOWNLOAD_ATTACHMENT,
- $file_name = null)
- {
- $cd = $disposition;
- if (isset($file_name)) {
- $cd .= '; filename="' . $file_name . '"';
- } elseif ($this->file) {
- $cd .= '; filename="' . basename($this->file) . '"';
- }
- $this->headers['Content-Disposition'] = $cd;
- }
-
- /**
- * Set content type of the download
- *
- * Default content type of the download will be 'application/x-octetstream'.
- * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) if
- * $content_type doesn't seem to be valid.
- *
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param string $content_type content type of file for download
- */
- function setContentType($content_type = 'application/x-octetstream')
- {
- if (!preg_match('/^[a-z]+\w*\/[a-z]+[\w.;= -]*$/', $content_type)) {
- return PEAR::raiseError(
- "Invalid content type '$content_type' supplied.",
- HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE
- );
- }
- $this->headers['Content-Type'] = $content_type;
- return true;
- }
-
- /**
- * Guess content type of file
- *
- * First we try to use PEAR::MIME_Type, if installed, to detect the content
- * type, else we check if ext/mime_magic is loaded and properly configured.
- *
- * Returns PEAR_Error if:
- * o if PEAR::MIME_Type failed to detect a proper content type
- * (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE)
- * o ext/magic.mime is not installed, or not properly configured
- * (HTTP_DOWNLOAD_E_NO_EXT_MMAGIC)
- * o mime_content_type() couldn't guess content type or returned
- * a content type considered to be bogus by setContentType()
- * (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE)
- *
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- */
- function guessContentType()
- {
- if (class_exists('MIME_Type') || @include_once 'MIME/Type.php') {
- if (PEAR::isError($mime_type = MIME_Type::autoDetect($this->file))) {
- return PEAR::raiseError($mime_type->getMessage(),
- HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE);
- }
- return $this->setContentType($mime_type);
- }
- if (!function_exists('mime_content_type')) {
- return PEAR::raiseError(
- 'This feature requires ext/mime_magic!',
- HTTP_DOWNLOAD_E_NO_EXT_MMAGIC
- );
- }
- if (!is_file(ini_get('mime_magic.magicfile'))) {
- return PEAR::raiseError(
- 'ext/mime_magic is loaded but not properly configured!',
- HTTP_DOWNLOAD_E_NO_EXT_MMAGIC
- );
- }
- if (!$content_type = @mime_content_type($this->file)) {
- return PEAR::raiseError(
- 'Couldn\'t guess content type with mime_content_type().',
- HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE
- );
- }
- return $this->setContentType($content_type);
- }
-
- /**
- * Send
- *
- * Returns PEAR_Error if:
- * o HTTP headers were already sent (HTTP_DOWNLOAD_E_HEADERS_SENT)
- * o HTTP Range was invalid (HTTP_DOWNLOAD_E_INVALID_REQUEST)
- *
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param bool $autoSetContentDisposition Whether to set the
- * Content-Disposition header if it isn't already.
- */
- function send($autoSetContentDisposition = true)
- {
- if (headers_sent()) {
- return PEAR::raiseError(
- 'Headers already sent.',
- HTTP_DOWNLOAD_E_HEADERS_SENT
- );
- }
-
- if (!ini_get('safe_mode')) {
- @set_time_limit(0);
- }
-
- if ($autoSetContentDisposition &&
- !isset($this->headers['Content-Disposition'])) {
- $this->setContentDisposition();
- }
-
- if ($this->cache) {
- $this->headers['ETag'] = $this->generateETag();
- if ($this->isCached()) {
- $this->HTTP->sendStatusCode(304);
- $this->sendHeaders();
- return true;
- }
- } else {
- unset($this->headers['Last-Modified']);
- }
-
- while (@ob_end_clean());
-
- if ($this->gzip) {
- @ob_start('ob_gzhandler');
- } else {
- ob_start();
- }
-
- $this->sentBytes = 0;
-
- if ($this->isRangeRequest()) {
- $this->HTTP->sendStatusCode(206);
- $chunks = $this->getChunks();
- } else {
- $this->HTTP->sendStatusCode(200);
- $chunks = array(array(0, $this->size));
- if (!$this->gzip && count(ob_list_handlers()) < 2) {
- $this->headers['Content-Length'] = $this->size;
- }
- }
-
- if (PEAR::isError($e = $this->sendChunks($chunks))) {
- ob_end_clean();
- $this->HTTP->sendStatusCode(416);
- return $e;
- }
-
- ob_end_flush();
- flush();
- return true;
- }
-
- /**
- * Static send
- *
- * @see HTTP_Download::HTTP_Download()
- * @see HTTP_Download::send()
- *
- * @static
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param array $params associative array of parameters
- * @param bool $guess whether HTTP_Download::guessContentType()
- * should be called
- */
- function staticSend($params, $guess = false)
- {
- $d = &new HTTP_Download();
- $e = $d->setParams($params);
- if (PEAR::isError($e)) {
- return $e;
- }
- if ($guess) {
- $e = $d->guessContentType();
- if (PEAR::isError($e)) {
- return $e;
- }
- }
- return $d->send();
- }
-
- /**
- * Send a bunch of files or directories as an archive
- *
- * Example:
- * <code>
- * require_once 'HTTP/Download.php';
- * HTTP_Download::sendArchive(
- * 'myArchive.tgz',
- * '/var/ftp/pub/mike',
- * HTTP_DOWNLOAD_TGZ,
- * '',
- * '/var/ftp/pub'
- * );
- * </code>
- *
- * @see Archive_Tar::createModify()
- * @deprecated use HTTP_Download_Archive::send()
- * @static
- * @access public
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param string $name name the sent archive should have
- * @param mixed $files files/directories
- * @param string $type archive type
- * @param string $add_path path that should be prepended to the files
- * @param string $strip_path path that should be stripped from the files
- */
- function sendArchive( $name,
- $files,
- $type = HTTP_DOWNLOAD_TGZ,
- $add_path = '',
- $strip_path = '')
- {
- require_once 'HTTP/Download/Archive.php';
- return HTTP_Download_Archive::send($name, $files, $type,
- $add_path, $strip_path);
- }
- // }}}
-
- // {{{ protected methods
- /**
- * Generate ETag
- *
- * @access protected
- * @return string
- */
- function generateETag()
- {
- if (!$this->etag) {
- if ($this->data) {
- $md5 = md5($this->data);
- } else {
- $fst = is_resource($this->handle) ?
- fstat($this->handle) : stat($this->file);
- $md5 = md5($fst['mtime'] .'='. $fst['ino'] .'='. $fst['size']);
- }
- $this->etag = '"' . $md5 . '-' . crc32($md5) . '"';
- }
- return $this->etag;
- }
-
- /**
- * Send multiple chunks
- *
- * @access protected
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param array $chunks
- */
- function sendChunks($chunks)
- {
- if (count($chunks) == 1) {
- return $this->sendChunk(current($chunks));
- }
-
- $bound = uniqid('HTTP_DOWNLOAD-', true);
- $cType = $this->headers['Content-Type'];
- $this->headers['Content-Type'] =
- 'multipart/byteranges; boundary=' . $bound;
- $this->sendHeaders();
- foreach ($chunks as $chunk){
- if (PEAR::isError($e = $this->sendChunk($chunk, $cType, $bound))) {
- return $e;
- }
- }
- #echo "\r\n--$bound--\r\n";
- return true;
- }
-
- /**
- * Send chunk of data
- *
- * @access protected
- * @return mixed Returns true on success or PEAR_Error on failure.
- * @param array $chunk start and end offset of the chunk to send
- * @param string $cType actual content type
- * @param string $bound boundary for multipart/byteranges
- */
- function sendChunk($chunk, $cType = null, $bound = null)
- {
- list($offset, $lastbyte) = $chunk;
- $length = ($lastbyte - $offset) + 1;
-
- if ($length < 1) {
- return PEAR::raiseError(
- "Error processing range request: $offset-$lastbyte/$length",
- HTTP_DOWNLOAD_E_INVALID_REQUEST
- );
- }
-
- $range = $offset . '-' . $lastbyte . '/' . $this->size;
-
- if (isset($cType, $bound)) {
- echo "\r\n--$bound\r\n",
- "Content-Type: $cType\r\n",
- "Content-Range: bytes $range\r\n\r\n";
- } else {
- if ($this->isRangeRequest()) {
- $this->headers['Content-Range'] = 'bytes '. $range;
- }
- $this->sendHeaders();
- }
-
- if ($this->data) {
- while (($length -= $this->bufferSize) > 0) {
- $this->flush(substr($this->data, $offset, $this->bufferSize));
- $this->throttleDelay and $this->sleep();
- $offset += $this->bufferSize;
- }
- if ($length) {
- $this->flush(substr($this->data, $offset, $this->bufferSize + $length));
- }
- } else {
- if (!is_resource($this->handle)) {
- $this->handle = fopen($this->file, 'rb');
- }
- fseek($this->handle, $offset);
- while (($length -= $this->bufferSize) > 0) {
- $this->flush(fread($this->handle, $this->bufferSize));
- $this->throttleDelay and $this->sleep();
- }
- if ($length) {
- $this->flush(fread($this->handle, $this->bufferSize + $length));
- }
- }
- return true;
- }
-
- /**
- * Get chunks to send
- *
- * @access protected
- * @return array
- */
- function getChunks()
- {
- $parts = array();
- foreach (explode(',', $this->getRanges()) as $chunk){
- list($o, $e) = explode('-', $chunk);
- if ($e >= $this->size || (empty($e) && $e !== 0 && $e !== '0')) {
- $e = $this->size - 1;
- }
- if (empty($o) && $o !== 0 && $o !== '0') {
- $o = $this->size - $e;
- $e = $this->size - 1;
- }
- $parts[] = array($o, $e);
- }
- return $parts;
- }
-
- /**
- * Check if range is requested
- *
- * @access protected
- * @return bool
- */
- function isRangeRequest()
- {
- if (!isset($_SERVER['HTTP_RANGE'])) {
- return false;
- }
- return $this->isValidRange();
- }
-
- /**
- * Get range request
- *
- * @access protected
- * @return array
- */
- function getRanges()
- {
- return preg_match('/^bytes=((\d*-\d*,? ?)+)$/',
- @$_SERVER['HTTP_RANGE'], $matches) ? $matches[1] : array();
- }
-
- /**
- * Check if entity is cached
- *
- * @access protected
- * @return bool
- */
- function isCached()
- {
- return (
- (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
- $this->lastModified == strtotime(current($a = explode(
- ';', $_SERVER['HTTP_IF_MODIFIED_SINCE'])))) ||
- (isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
- $this->compareAsterisk('HTTP_IF_NONE_MATCH', $this->etag))
- );
- }
-
- /**
- * Check if entity hasn't changed
- *
- * @access protected
- * @return bool
- */
- function isValidRange()
- {
- if (isset($_SERVER['HTTP_IF_MATCH']) &&
- !$this->compareAsterisk('HTTP_IF_MATCH', $this->etag)) {
- return false;
- }
- if (isset($_SERVER['HTTP_IF_RANGE']) &&
- $_SERVER['HTTP_IF_RANGE'] !== $this->etag &&
- strtotime($_SERVER['HTTP_IF_RANGE']) !== $this->lastModified) {
- return false;
- }
- if (isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE'])) {
- $lm = current($a = explode(';', $_SERVER['HTTP_IF_UNMODIFIED_SINCE']));
- if (strtotime($lm) !== $this->lastModified) {
- return false;
- }
- }
- if (isset($_SERVER['HTTP_UNLESS_MODIFIED_SINCE'])) {
- $lm = current($a = explode(';', $_SERVER['HTTP_UNLESS_MODIFIED_SINCE']));
- if (strtotime($lm) !== $this->lastModified) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Compare against an asterisk or check for equality
- *
- * @access protected
- * @return bool
- * @param string key for the $_SERVER array
- * @param string string to compare
- */
- function compareAsterisk($svar, $compare)
- {
- foreach (array_map('trim', explode(',', $_SERVER[$svar])) as $request) {
- if ($request === '*' || $request === $compare) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Send HTTP headers
- *
- * @access protected
- * @return void
- */
- function sendHeaders()
- {
- foreach ($this->headers as $header => $value) {
- $this->HTTP->setHeader($header, $value);
- }
- $this->HTTP->sendHeaders();
- /* NSAPI won't output anything if we did this */
- if (strncasecmp(PHP_SAPI, 'nsapi', 5)) {
- ob_flush();
- flush();
- }
- }
-
- /**
- * Flush
- *
- * @access protected
- * @return void
- * @param string $data
- */
- function flush($data = '')
- {
- if ($dlen = strlen($data)) {
- $this->sentBytes += $dlen;
- echo $data;
- }
- ob_flush();
- flush();
- }
-
- /**
- * Sleep
- *
- * @access protected
- * @return void
- */
- function sleep()
- {
- if (OS_WINDOWS) {
- com_message_pump($this->throttleDelay);
- } else {
- usleep($this->throttleDelay * 1000);
- }
- }
- // }}}
- }
- ?>
-